package obecto.component.scrollBar
{
	import flash.display.DisplayObject;
	import flash.events.Event;
	import flash.events.MouseEvent;
	
	import mx.utils.StringUtil;
	
	import obecto.component.button.Button;
	import obecto.core.FLContainer;

	[Event(name="scrollPositionChange", type="flash.events.Event")]
	
	public class ScrollBar extends FLContainer
	{
		public var track : Button;
		public var thumb : Button;
		
		protected var _minRange : Number = 0;
		[Bindable]
		public function set minRange(value : Number) : void
		{
			_minRange = value;
			
			if(initialized)
			{
				resizeThumb();
			}
		}
		public function get minRange() : Number
		{
			return _minRange;
		}
		
		protected var _maxRange : Number = 0;
		[Bindable]
		public function set maxRange(value : Number) : void
		{
			_maxRange = value;
			
			if(initialized)
			{
				resizeThumb();
			}
		}
		public function get maxRange() : Number
		{
			return _maxRange;
		}
		
		protected var _pageSize : Number = 0;
		[Bindable]
		public function set pageSize(value : Number) : void
		{
			_pageSize = value;
			
			if(initialized)
			{
				resizeThumb();
			}
		}
		public function get pageSize() : Number
		{
			return _pageSize;
		}
		
		protected var _enabled : Boolean = true;
		[Bindable]
		public function set enabled(value : Boolean) : void
		{
			_enabled = value;
			
			if(_enabled)
			{
				track.enabled = true;
				track.mouseEnabled = true;
				track.mouseChildren = true;
				
				thumb.visible = true;
				thumb.mouseEnabled = true;
				thumb.mouseChildren = true;
			}
			else
			{
				track.enabled = false;
				track.mouseEnabled = false;
				track.mouseChildren = false;
				
				thumb.visible = false;
				thumb.mouseEnabled = false;
				thumb.mouseChildren = false;
			}
		}
		public function get enabled() : Boolean
		{
			return _enabled;
		}
		
		protected var _scrollPosition : Number = 0;
		[Bindable("scrollPositionChange")]
		[Bindable("scrollPositionChangeProgrammatically")]
		public function set scrollPosition(value : Number) : void
		{	
			if(initialized)
			{
				setScrollPositionActual(value, true, true);
			}
			else
			{
				_scrollPosition = value;
			}
		}
		public function get scrollPosition() : Number
		{
			return _scrollPosition;
		}
		
		protected var thumbMinAxisValue : Number = 0;
		protected var thumbMaxAxisValue : Number = 0;
		
		protected var draggingThumb : Boolean = false;
		protected var mouseThumbAxisGrabValue : Number = 0;
		
		private var _axis : String;
		protected final function get axis() : String
		{
			return _axis;
		}
		
		private var _scrollDirection : String = ScrollBarDirection.VERTICAL;
		[Inspectable(defaultValue="vertical", enumeration="vertical,horizontal")]
		public final function set scrollDirection(value : String) : void
		{
			if(initialized)
			{
				throw new Error("scrollDirection cannot be set after the component has been initialized.");
			}
			else
			{
				_scrollDirection = value;
			}
		}
		public final function get scrollDirection() : String
		{
			return _scrollDirection;
		}
		
		public function ScrollBar()
		{
			super();
		}			
		
		protected function dispatchScrollPositionChange() : void
		{
			dispatchEvent(new Event("scrollPositionChange"));
		}
		
		protected function dispatchScrollPositionChangeProgrammatically() : void
		{
			dispatchEvent(new Event("scrollPositionChangeProgrammatically"));
		}
		
		protected function setScrollPositionActual(scrollPosition : Number, moveThumb : Boolean, isChangeProgrammatic : Boolean) : void
		{
			//Fix wrong scroll position
			if(minRange < maxRange)
			{
				if(scrollPosition > maxRange)
				{
					scrollPosition = maxRange;
				}
				else if(scrollPosition < minRange)
				{
					scrollPosition = minRange;
				}
				
				enabled = true;
			}
			else
			{
				scrollPosition = minRange;
				
				enabled = false;
			}
			
			_scrollPosition = scrollPosition;
			
			//Move the thumb if needed
			if(moveThumb)
			{
				moveThumbToScrollPosition(scrollPosition);
			}
			
			//Dispatch scroll position change
			if(isChangeProgrammatic)
			{
				dispatchScrollPositionChangeProgrammatically();
			}
			else
			{
				dispatchScrollPositionChange();
			}
		}
		
		protected function resizeThumb() : void
		{
			switch(scrollDirection)
			{
				case ScrollBarDirection.VERTICAL:
				{
					thumb.height = pageSize / (pageSize + (maxRange - minRange)) * track.height;
					break;
				}
				case ScrollBarDirection.HORIZONTAL:
				{
					thumb.width = pageSize / (pageSize + (maxRange - minRange)) * track.width;
					break;
				}
			}
			
			recalculateThumbAxisExtremumValues();
			setScrollPositionActual(scrollPosition, true, true);
		}
		
		protected function moveThumbToScrollPosition(scrollPosition : Number) : void
		{
			if(minRange < maxRange)
			{
				if(scrollPosition >= maxRange)
				{
					thumb[axis] = thumbMaxAxisValue;
				}
				else if(scrollPosition <= minRange)
				{
					thumb[axis] = thumbMinAxisValue;
				}
				else
				{
					thumb[axis] = (thumbMaxAxisValue - thumbMinAxisValue) * (scrollPosition / (maxRange - minRange));
				}
			}
			else
			{
				thumb[axis] = thumbMinAxisValue;
			}
		}
		
		override protected function initialize() : void
		{	
			contentChildren = [track, thumb];
		}
		
		override protected function creationComplete() : void
		{
			determineAxis();
			positionElements();
			resizeThumb();
			recalculateThumbAxisExtremumValues();
			
			setScrollPositionActual(_scrollPosition, true, true);
			
			thumb.addEventListener(MouseEvent.MOUSE_DOWN, handleThumbMouseDown);
			
			super.creationComplete();	
		}
		
		protected function determineAxis() : void
		{
			switch(_scrollDirection)
			{
				case ScrollBarDirection.VERTICAL :
				{
					_axis = "y";
					break;
				}
				
				case ScrollBarDirection.HORIZONTAL :
				{
					_axis = "x";
					break;
				}
				
				default :
				{
					throw new Error(StringUtil.substitute("Unknow scrollDirection value {0}", _scrollDirection));
					break;
				}
			}
		}
		
		protected function recalculateThumbAxisExtremumValues() : void
		{
			thumbMinAxisValue = 0;
			
			switch(scrollDirection)
			{
				case ScrollBarDirection.VERTICAL :
				{
					thumbMaxAxisValue = Math.max(0, track.height - thumb.height);
					break;
				}
				case ScrollBarDirection.HORIZONTAL :
				{
					thumbMaxAxisValue = Math.max(0, track.width - thumb.width);
					break;
				}
			}
		}
		
		protected function positionElements() : void
		{
			track.x = 0;
			track.y = 0;
			
			thumb.x = 0;
			thumb.y = 0;
			
			switch(scrollDirection)
			{
				case ScrollBarDirection.VERTICAL :
				{
					thumb.x = (track.width - thumb.width) / 2;
					break;
				}
				case ScrollBarDirection.HORIZONTAL :
				{
					thumb.y = (track.height - thumb.height) / 2;
					break;			
				}
			}
		}
		
		protected function handleThumbMouseDown(event : MouseEvent) : void
		{
			mouseThumbAxisGrabValue = getMouseLocalAxisValue(thumb);
			
			draggingThumb = true;
			stage.addEventListener(MouseEvent.MOUSE_MOVE, handleStageMouseMove);
			stage.addEventListener(MouseEvent.MOUSE_UP, handleStageMouseUp);
		}
		
		protected function getMouseLocalAxisValue(target : DisplayObject) : Number
		{	
			switch(axis)
			{
				case "x" :
					return target.mouseX;
				case "y" :
					return target.mouseY;
				default :
					throw new Error(StringUtil.substitute("Unknown axis value {0}", axis));
			}
		}
		protected function getMouseStageAxisValue() : Number
		{
			switch(axis)
			{
				case "x" :
					return stage.mouseX;
				case "y" :
					return stage.mouseY;
				default :
					throw new Error(StringUtil.substitute("Unknown axis value {0}", axis));
			}			
		}
		
		protected function handleStageMouseUp(event : MouseEvent) : void
		{
			draggingThumb = false;
			stage.removeEventListener(MouseEvent.MOUSE_MOVE, handleStageMouseMove);
			stage.removeEventListener(MouseEvent.MOUSE_UP, handleStageMouseUp);
		}
		
		protected function handleStageMouseMove(event : MouseEvent) : void
		{
			if(draggingThumb && event.buttonDown)
			{
				var mouseThumbAxisValue : Number = getMouseLocalAxisValue(thumb);
				var thumbAxisValue : Number = thumb[axis] + mouseThumbAxisValue - mouseThumbAxisGrabValue;
				
				if(thumbAxisValue < thumbMinAxisValue)
				{
					thumbAxisValue = thumbMinAxisValue;
				}
				else if(thumbAxisValue > thumbMaxAxisValue)
				{
					thumbAxisValue = thumbMaxAxisValue;
				}
				
				thumb[axis] = thumbAxisValue;
				
				
				var scrollPos : Number
				if(minRange < maxRange)
				{
					scrollPos = minRange + (maxRange - minRange) *
						((thumbAxisValue - thumbMinAxisValue) / (thumbMaxAxisValue - thumbMinAxisValue))
				}
				else
				{
					scrollPos = minRange;
				}
				
				setScrollPositionActual(scrollPos, false, false);
			}
		}
	}
}